﻿// ------------------------------------------------------------------------
// 项目名称：Canroc.Net 
// 版权归属：Canroc（https://gitee.com/canroc）
//
// 许可证信息
// Canroc.Net项目主要遵循 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-APACHE 文件。
//
// 使用条款：
// 1.使用本项目应遵守相关法律法规和许可证的要求。
// 2.不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动。
// 3.任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任
//
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
//
// 其他重要信息
// Canroc.Net 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// ------------------------------------------------------------------------

using Canroc.Net.Application.SysParamService;
using Canroc.Net.Application.SysTemplateService;
using Canroc.Net.Application.SysUserService.Dto;
using System.Data;
using System.Net.Mime;

// ReSharper disable once CheckNamespace
namespace Canroc.Net.Application.SysUserService;

/// <summary>
///     系统用户应用服务
/// </summary>
[ApiDescriptionSettings("System")]
public partial class SysUserAppService(
    SqlSugarRepository<SysUser> userRep,
    SqlSugarRepository<RelOrgUser> orgAndUserRep,
    SqlSugarRepository<RelRoleUser> roleAndUserRep,
    IServiceProvider serviceProvider) : IDynamicApiController, ITransient
{
    /// <summary>
    ///     添加系统用户
    /// </summary>
    /// <param name="data">表单数据</param>
    /// <returns></returns>
    [UnitOfWork]
    public async Task<bool> AdditionAsync(UserInput data)
    {
        // 账号验证
        var isExist = await ExistAsync(data.Account);
        if (isExist)
            throw Oops.Oh(ErrorCodeEnum.AccountExist);

        // 填装数据
        var entity = data.Adapt<SysUser>();
        entity.Id = Guid.NewGuid();
        // 默认昵称
        if (entity.NickName.IsNullOrEmpty())
            entity.NickName = data.Name;

        // 默认密码
        var secretKeyOption = App.GetOptions<SecretKeyOption>();
        var sysParamAppService = serviceProvider.GetRequiredService<SysParamAppService>();
        var (hasPasswordValue, passwordParam) =
            await sysParamAppService.GetDataAsync(ParamConst.SystemUserPassword);
        entity.PassWord = hasPasswordValue
            ? passwordParam!.Value.Sm2Sign(secretKeyOption.Sm2PrivateKey, entity.Account)
            : ParamConst.DefaultPassword.Sm2Sign(secretKeyOption.Sm2PrivateKey, entity.Account);
        // 用户默认头像
        var (hasAvatarValue, avatarParam) = await sysParamAppService.GetDataAsync(ParamConst.SystemUserAvatar);
        entity.Avatar = hasAvatarValue ? avatarParam!.Value : string.Empty;

        // 保存关联组织机构
        RelOrgUser iou = new() { OrgId = data.OrgId, UserId = entity.Id };

        // 保存角色信息
        List<RelRoleUser> list = [];
        data.RoleIds.ForEach(roleId =>
        {
            list.Add(new RelRoleUser { RoleId = roleId, UserId = entity.Id });
        });

        // 数据库写操作
        return await userRep.InsertAsync(entity) &&
               await orgAndUserRep.InsertAsync(iou) &&
               await roleAndUserRep.InsertRangeAsync(list);
    }

    /// <summary>
    ///     编辑系统用户
    /// </summary>
    /// <param name="data">表单数v 据</param>
    /// <returns></returns>
    [UnitOfWork]
    public async Task<bool> EditAsync(UserInput data)
    {
        var entity = await userRep.GetByIdAsync(data.Id);
        // 更新数据
        data.Adapt(entity);

        // 保存关联组织机构
        RelOrgUser iou = new() { OrgId = data.OrgId, UserId = entity.Id };

        // 保存角色信息
        List<RelRoleUser> list = [];
        data.RoleIds.ForEach(roleId =>
        {
            list.Add(new RelRoleUser { RoleId = roleId, UserId = entity.Id });
        });

        // 数据库写操作
        return await userRep.UpdateAsync(entity) &&
               await orgAndUserRep.DeleteAsync(i => i.UserId == entity.Id) &&
               await orgAndUserRep.InsertAsync(iou) &&
               await roleAndUserRep.DeleteAsync(i => i.UserId == entity.Id) &&
               await roleAndUserRep.InsertRangeAsync(list);
    }

    /// <summary>
    ///     编辑系统用户基本信息
    /// </summary>
    /// <param name="data">表单数据</param>
    /// <returns></returns>
    public async Task<bool> EditBasicAsync(UserInput data)
    {
        return await userRep.UpdateAsync(
            u => new SysUser
            {
                Name = data.Name,
                NickName = data.NickName,
                Sex = data.Sex,
                Phone = data.Phone,
                Email = data.Email,
                SelfIntroduction = data.SelfIntroduction
            }, u => u.Id == data.Id);
    }

    /// <summary>
    ///     编辑系统用户头像
    /// </summary>
    /// <param name="data">表单数据</param>
    /// <returns></returns>
    public async Task<bool> EditAvatarAsync(UserAvatarInput data)
    {
        return await userRep.UpdateAsync(u => new SysUser { Avatar = data.Avatar }, u => u.Id == data.UserId);
    }

    /// <summary>
    ///     编辑系统用户状态
    /// </summary>
    /// <param name="data">请求参数</param>
    /// <returns></returns>
    public async Task<bool> EnableAsync(InputModel data)
    {
        return await userRep.UpdateAsync(u => new SysUser { IsEnable = SqlFunc.IIF(u.IsEnable, false, true) },
            u => u.Id == data.Id);
    }

    /// <summary>
    ///     解除系统用户账号锁定
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public async Task<bool> UnlockAsync(InputModel data)
    {
        return await EditErrorNum(data.Id, 0);
    }

    /// <summary>
    ///     重置系统用户密码
    /// </summary>
    /// <param name="data">请求参数</param>
    /// <returns></returns>
    public async Task<bool> ResetAsync(UserPasswordInput data)
    {
        var secretKeyOption = App.GetOptions<SecretKeyOption>();
        string password;
        if (data.PassWord.IsNullOrEmpty())
        {
            var sysParamAppService = serviceProvider.GetRequiredService<SysParamAppService>();
            var (hasValue, param) = await sysParamAppService.GetDataAsync(ParamConst.SystemUserPassword);
            password = hasValue ? param!.Value : ParamConst.DefaultPassword;
        }
        else
        {
            password = data.PassWord.Sm2Decrypt(secretKeyOption.Sm2PrivateKey);
        }

        var user = await userRep.GetByIdAsync(data.Id);
        user.PassWord = password.Sm2Sign(secretKeyOption.Sm2PrivateKey, user.Account);
        return await userRep.UpdateAsync(user);
    }

    /// <summary>
    ///     删除系统用户
    /// </summary>
    /// <param name="data">请求参数</param>
    /// <returns></returns>
    public async Task<bool> DelAsync(DeleteInputModel data)
    {
        return await userRep.UpdateAsync(u => new SysUser { IsDelete = true }, u => data.Ids.Contains(u.Id));
    }

    /// <summary>
    ///     导入: 批量创建系统用户
    /// </summary>
    /// <param name="data">请求参数</param>
    /// <returns></returns>
    [UnitOfWork]
    public async Task<bool> ImportAsync([FromForm] UserImportInput data)
    {
        if (data.File is null)
            throw Oops.Oh(ErrorCodeEnum.NotFile);

        // 文件扩展名校验
        var importFileExtend = Path.GetExtension(data.File.FileName).ToLower();
        if (importFileExtend != ".xlsx")
            throw Oops.Oh(ErrorCodeEnum.FileTypeError);

        // 读取上传的文件
        var importDt = NpoiHelper.QueryAsDataTable(data.File.OpenReadStream());

        // 查询批量建户的模板，检查上传的文件与模板是否一致
        var sysTemplateAppService = serviceProvider.GetRequiredService<SysTemplateAppService>();
        var template = await sysTemplateAppService.GetDataAsync(TemplateConst.SystemUserImport);
        _ = template ?? throw Oops.Oh(ErrorCodeEnum.NotFile);

        await using var templateFile = File.OpenRead(template.FilePath);
        // 此处第二个参数设为false 因为true时有BUG 对不够两条的数据无法生成DataTable的列头
        var templateDt = NpoiHelper.QueryAsDataTable(templateFile);
        if (!DataTableHelper.Compare(importDt, templateDt))
            throw Oops.Oh(ErrorCodeEnum.TemplateMismatch);

        // 批量添加用户
        List<SysUser> users = [];
        List<RelOrgUser> ious = [];
        List<RelRoleUser> irus = [];
        List<string> roleIds = [.. data.RoleIds.Split(",")];
        var secretKeyOption = App.GetOptions<SecretKeyOption>();
        // 默认密码
        var sysParamAppService = serviceProvider.GetRequiredService<SysParamAppService>();
        var (hasPasswordValue, passwordParam) =
            await sysParamAppService.GetDataAsync(ParamConst.SystemUserPassword);
        var defaultPassword = hasPasswordValue ? passwordParam!.Value : ParamConst.DefaultPassword;
        // 用户默认头像
        var (hasAvatarValue, avatarParam) = await sysParamAppService.GetDataAsync(ParamConst.SystemUserAvatar);
        var defaultAvatar = hasAvatarValue ? avatarParam!.Value : string.Empty;
        foreach (DataRow row in importDt.Rows)
        {
            var userId = Guid.NewGuid();
            users.Add(new SysUser
            {
                Id = userId,
                Account = row["账户名称"].ToString()!,
                PassWord = defaultPassword.Sm2Sign(secretKeyOption.Sm2PrivateKey, row["账户名称"].ToString()!),
                Name = row["用户名称"].ToString()!,
                NickName = row["用户名称"].ToString()!,
                Sex = row["性别"].ToString()! switch
                {
                    "未知" => UserSexEnum.Unknown,
                    "男" => UserSexEnum.Boy,
                    "女" => UserSexEnum.Grail,
                    _ => UserSexEnum.Unknown
                },
                Avatar = defaultAvatar,
                Phone = row["手机号码"].ToString()!,
                Email = row["邮箱"].ToString()!
            });

            // 保存关联组织机构
            ious.Add(new RelOrgUser { OrgId = data.OrgId, UserId = userId });

            // 保存角色信息
            roleIds.ForEach(roleId =>
            {
                irus.Add(new RelRoleUser { RoleId = Guid.Parse(roleId), UserId = userId });
            });
        }

        // 数据库写操作
        return await userRep.InsertRangeAsync(users) &&
               await orgAndUserRep.InsertRangeAsync(ious) &&
               await roleAndUserRep.InsertRangeAsync(irus);
    }

    /// <summary>
    ///     获取系统用户
    /// </summary>
    /// <param name="id">主键</param>
    /// <returns></returns>
    public async Task<UserOutput?> GetDataAsync(Guid id)
    {
        return await userRep
            .AsQueryable()
            .LeftJoin<RelOrgUser>((u, i) => u.Id == i.UserId)
            .LeftJoin<SysOrg>((u, i, o) => i.OrgId == o.Id)
            .Where(u => u.Id == id)
            .OrderBy(u => u.CreateTime)
            .Select((u, i, o) => new UserOutput
            {
                Name = u.Name,
                OrgId = o.Id,
                OrgName = o.Name,
                RoleIds = SqlFunc.Subqueryable<RelRoleUser>()
                    .LeftJoin<SysRole>((iru, r) => iru.RoleId == r.Id && r.IsEnable && !r.IsDelete)
                    .Where((iru, r) => iru.UserId == u.Id)
                    .ToList(iru => iru.RoleId),
                RoleName = SqlFunc.Subqueryable<RelRoleUser>()
                    .LeftJoin<SysRole>((iru, r) => iru.RoleId == r.Id && r.IsEnable && !r.IsDelete)
                    .Where((iru, r) => iru.UserId == u.Id)
                    .SelectStringJoin((iru, r) => r.Name, ",")
            }, true)
            .FirstAsync();
    }

    /// <summary>
    ///     根据角色编码获取用户选项
    /// </summary>
    /// <param name="code">角色编码</param>
    /// <returns></returns>
    [Route("select-option/role/{code}")]
    public async Task<List<SelectOptionOutputModel>?> GetUerOptionByRoleAsync(string code)
    {
        return await roleAndUserRep
            .AsQueryable()
            .LeftJoin<SysRole>((iru, r) => iru.RoleId == r.Id && r.IsEnable)
            .LeftJoin<SysUser>((iru, r, u) => iru.UserId == u.Id && u.IsEnable)
            .Where((iru, r, u) => r.Code == code)
            .Select((iru, r, u) => new SelectOptionOutputModel { Label = u.Name, Value = u.Id })
            .ToListAsync();
    }

    /// <summary>
    ///     获取所有用户信息
    /// </summary>
    /// <returns></returns>
    public async Task<List<UserInfoOutput>?> GetAllUserAsync()
    {
        return await userRep
            .AsQueryable()
            .LeftJoin<RelOrgUser>((u, rou) => u.Id == rou.UserId)
            .LeftJoin<SysOrg>((u, rou, o) => rou.OrgId == o.Id)
            .Where(u => !u.IsAdmin)
            .Select((u, rou, o) => new UserInfoOutput
            {
                UserId = u.Id,
                Org = o.Name,
                Roles = SqlFunc.Subqueryable<RelRoleUser>()
                    .LeftJoin<SysRole>((iru, r) => iru.RoleId == r.Id && r.IsEnable && !r.IsDelete)
                    .Where((iru, r) => iru.UserId == u.Id)
                    .ToList((iru, r) => r.Name)
            }, true)
            .ToListAsync();
    }

    /// <summary>
    ///     分页: 系统用户集合
    /// </summary>
    /// <param name="query">查询参数</param>
    /// <returns></returns>
    public async Task<PageModel<UserOutput>> GetPageAsync([FromQuery] UserPageQuery query)
    {
        return await SearchUserAsync(query);
    }

    /// <summary>
    ///     导出: 系统用户集合
    /// </summary>
    /// <param name="query">查询参数</param>
    /// <returns></returns>
    [HttpGet]
    [NonUnify]
    [SuppressMonitor]
    public async Task<IActionResult> ExportAsync([FromQuery] UserPageQuery query)
    {
        // 查询数据
        var result = await SearchUserAsync(query, false) as List<UserOutput> ?? [];
        if (result.Count <= 0) throw Oops.Oh(ErrorCodeEnum.NotData);

        // 查询导出模版
        var sysTemplateAppService = serviceProvider.GetRequiredService<SysTemplateAppService>();
        var template = await sysTemplateAppService.GetDataAsync(TemplateConst.SystemUserExport);
        _ = template ?? throw Oops.Oh(ErrorCodeEnum.NotFile);

        // 生成Excel
        var workbook = await NpoiHelper.GenerateExcelAsync(template.FilePath, result);
        var stream = new MemoryStream();
        workbook.Write(stream, true);
        stream.Position = 0;

        // 返回文件流
        return new FileStreamResult(stream, MediaTypeNames.Application.Octet) { FileDownloadName = "系统用户信息.xlsx" };
    }

    #region 内部函数

    /// <summary>
    ///     根据账号获取用户信息
    /// </summary>
    /// <param name="account">账号</param>
    /// <returns></returns>
    [NonAction]
    public async Task<ValidateUserOutput?> GetDataAsync(string account)
    {
        return await userRep
            .AsQueryable()
            .Where(u => u.Account == account)
            .OrderBy(u => u.CreateTime)
            .Select(u => new ValidateUserOutput
            {
                Name = u.Name,
                RoleIds = SqlFunc.Subqueryable<RelRoleUser>()
                    .LeftJoin<SysRole>((iru, r) => iru.RoleId == r.Id && r.IsEnable && !r.IsDelete)
                    .Where((iru, r) => iru.UserId == u.Id)
                    .ToList(iru => iru.RoleId),
                RoleCodes = SqlFunc.Subqueryable<RelRoleUser>()
                    .LeftJoin<SysRole>((iru, r) => iru.RoleId == r.Id && r.IsEnable && !r.IsDelete)
                    .Where((iru, r) => iru.UserId == u.Id)
                    .SelectStringJoin((iru, r) => r.Code, ",")
            }, true)
            .FirstAsync();
    }

    /// <summary>
    ///     账号是否已创建
    /// </summary>
    /// <param name="account">用户账号</param>
    /// <returns></returns>
    [NonAction]
    private async Task<bool> ExistAsync(string account)
    {
        return await userRep.IsAnyAsync(u => u.Account == account);
    }

    /// <summary>
    ///     编辑系统用户输错次数
    /// </summary>
    /// <param name="id">主键</param>
    /// <param name="num">输错次数</param>
    /// <returns></returns>
    [NonAction]
    public async Task<bool> EditErrorNum(Guid id, int num)
    {
        return await userRep.UpdateAsync(u => new SysUser { ErrorNum = num }, u => u.Id == id);
    }

    /// <summary>
    ///     获取用户角色编码集合
    /// </summary>
    /// <param name="id">主键</param>
    /// <returns></returns>
    [NonAction]
    public async Task<List<string>?> GetUserRoleCodesAsync(Guid id)
    {
        return await roleAndUserRep
            .AsQueryable()
            .LeftJoin<SysRole>((iru, r) => iru.RoleId == r.Id && r.IsEnable)
            .Where((iru, r) => iru.UserId == id)
            .Select((iru, r) => r.Code)
            .ToListAsync();
    }

    /// <summary>
    ///     搜索系统用户
    /// </summary>
    /// <param name="query">请求参数</param>
    /// <param name="isPage">是否分页</param>
    /// <returns></returns>
    [NonAction]
    private async Task<dynamic> SearchUserAsync(UserPageQuery query, bool isPage = true)
    {
        var selectSql = userRep
            .AsQueryable()
            .LeftJoin<RelOrgUser>((u, rou) => u.Id == rou.UserId)
            .LeftJoin<SysOrg>((u, rou, o) => rou.OrgId == o.Id)
            .Where(u => !u.IsAdmin)
            .WhereIF(!query.NickName.IsNullOrEmpty(), u => u.NickName.Contains(query.NickName))
            .WhereIF(query.Sex.HasValue, u => u.Sex == query.Sex)
            .WhereIF(query.IsEnable.HasValue, u => u.IsEnable == query.IsEnable)
            .WhereIF(query.OrgId.HasValue, (u, rou, o) => rou.OrgId == query.OrgId)
            .Select((u, rou, o) => new UserOutput
            {
                Name = u.Name,
                RoleIds = SqlFunc.Subqueryable<RelRoleUser>()
                    .LeftJoin<SysRole>((iru, r) => iru.RoleId == r.Id && r.IsEnable && !r.IsDelete)
                    .Where((iru, r) => iru.UserId == u.Id)
                    .ToList(iru => iru.RoleId),
                RoleName = SqlFunc.Subqueryable<RelRoleUser>()
                    .LeftJoin<SysRole>((iru, r) => iru.RoleId == r.Id && r.IsEnable && !r.IsDelete)
                    .Where((iru, r) => iru.UserId == u.Id)
                    .SelectStringJoin((iru, r) => r.Name, ","),
                OrgName = o.Name
            }, true)
            .MergeTable()
            .OrderByPropertyName(query.OrderField, query.IsAscending ? OrderByType.Asc : OrderByType.Desc);

        return isPage ? await selectSql.ToPageAsync(query.PageIndex, query.PageSize) : await selectSql.ToListAsync();
    }

    #endregion
}